/*
 * client.h
 *
 *  Created on: Jun 8, 2012
 *      Author: avihay
 */

#ifndef CLIENT_H_
#define CLIENT_H_

#include "../protocol/protocol.h"
#include "common_defs.h"
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <string>

typedef enum
{
	COMMAND_FOLLOW = 0,
	COMMAND_UNFOLLOW,
	COMMAND_DISCONNECT,
	COMMAND_TWIT,
	COMMAND_DIRECT_MESSAGE,
	COMMAND_PUBLIC_ANNOUNCEMENT,
	COMMAND_WHO,
	COMMAND_UNKNOWN
} command_instruction;

typedef struct
{
	command_instruction	cmd;
	std::string			arg;
} command_t;


class Client
{
public:
	explicit	Client(const std::string &client_name, const std::string &server_address , int port);

	/*
	 * result_t	start_client();
	 *
	 * Starts the client. This method does not return until the client
	 * shuts down or there is an error message.
	 *
	 * Returns: OPERATION_SUCCESS if the client closed successfuly the
	 * session. Errors will be returned in other cases.
	 */
	result_t	start_client();

	~Client();

private:

	// *****************************************************************
	// ************* COMMAND IMPLEMENTATION ****************************
	// *****************************************************************

	/*
	 * result_t	follow(const command_t &cmd);
	 *
	 * Process a given command as a FOLLOW command and send it to the
	 * server.
	 * The method checks for the correct number of arguments.
	 *
	 * Returns: OPERATION_SUCCESS if the message was sent succeesfully.
	 * Errors will be returned in other cases.
	 */
	result_t	follow(const command_t &cmd);

	/*
	 * result_t	unfollow(const command_t &cmd);
	 *
	 * Process a given command as an UNFOLLOW command and send it to the
	 * server.
	 * The method checks for the correct number of arguments.
	 *
	 * Returns: OPERATION_SUCCESS if the message was sent succeesfully.
	 * Errors will be returned in other cases.
	 */
	result_t	unfollow(const command_t &cmd);

	/*
	 * result_t	twit(const command_t &cmd);
	 *
	 * Process a given command as a TWIT command and send it to the
	 * server.
	 * The method checks for the correct number of arguments.
	 *
	 * Returns: OPERATION_SUCCESS if the message was sent succeesfully.
	 * Errors will be returned in other cases.
	 */
	result_t	twit(const command_t &cmd);

	 /* result_t	direct_message(const command_t &cmd);
	 *
	 * Process a given command as a DM command and send it to the
	 * server.
	 * The method checks for the correct number of arguments.
	 *
	 * Returns: OPERATION_SUCCESS if the message was sent succeesfully.
	 * Errors will be returned in other cases.
	 */
	result_t	direct_message(const command_t &cmd);

	/*
	 * result_t	public_announce(const command_t &cmd);
	 *
	 * Process a given command as a PA command and send it to the
	 * server.
	 * The method checks for the correct number of arguments.
	 *
	 * Returns: OPERATION_SUCCESS if the message was sent succeesfully.
	 * Errors will be returned in other cases.
	 */
	result_t	public_announce(const command_t &cmd);

	/*
	 * result_t	who(const command_t &cmd);
	 *
	 * Process a given command as a WHO command and send it to the
	 * server.
	 * The method checks for the correct number of arguments.
	 *
	 * Returns: OPERATION_SUCCESS if the message was sent succeesfully.
	 * Errors will be returned in other cases.
	 */
	result_t	who(const command_t &cmd);

	/*
	 * result_t	exit_command(const command_t &cmd);
	 *
	 * Process a given command as a EXIT command and send it to the
	 * server.
	 * The method checks for the correct number of arguments.
	 *
	 * Returns: OPERATION_SUCCESS if the message was sent succeesfully.
	 * Errors will be returned in other cases.
	 */
	result_t	exit_client(const command_t &cmd);

	// *****************************************************************
	// ***************** AUXILARY FUNCTIONS ****************************
	// *****************************************************************

	// Opens a new client socket to a given server
	result_t	open_client_socket(int server_port, std::string server_address, int &socket);

	// Sends a message to the currently connected server
	result_t	send_msg_to_server(int msg_length, messages_type_t type, const char* msg_data);

	// Recieve a message from the server and place it in the given message
	result_t	get_msg_from_server(in_msg_t &msg_from_server);

	// Build a command object from a string
	result_t	build_command_from_string(const std::string &input, command_t &cmd  );

	// *****************************************************************
	// ****************** DATA MEMBERS *********************************
	// *****************************************************************

	// Client paramteres
	std::string 	_client_name;	// Store the client name as string
	bool			_is_client_on;

	// Connection parameters
	int 			_socket_fd;		// Socket filedescriptor
	std::string		_server_add;	// Server address
	int				_server_port;

	// File descriptors for select
	fd_set			_rd_fd_set; 	// FD for read select
	fd_set			_wr_fd_set; 	// FD for write select
	fd_set			_ex_fd_set; 	// FD for exception select

	//
	bool			_is_msg_completed;
	uint32_t		_bytes_left_to_read;
};



#endif /* CLIENT_H_ */
